package co.edu.usbcali.egrapico.logica;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Set;
import java.util.Iterator;
import java.util.Map;
import java.lang.Object;
import java.util.Vector;

/**
 * Clase para realizar el an�lisis sem�ntico del c�digo <I>PiCO</I> resultante
 * despu�s de la traducci�n. Provee m�todos que son usados a lo largo del
 * proceso de traducci�n del c�digo <I>GraPiCO</I> con el fin de poder
 * determinar al final del proceso en donde se presenta un <I>Error</I> o una
 * <I>Advertencia</I>.
 *
 * @author Johanna Noguera Le�n & Alejandro Berm�dez Mart�nez.
 * @version 1.0
 * @since Marzo de 2005
 */
public class SemanticAnalizer {

    // N�mero consecutivo del mensaje a
    // ingresar en la tabla hash de mensajes sem�nticos.
    static int currentSemanticMessage = 0;
    // N�mero consecutivo del mensaje a
    // ingresar en la tabla hash de mensajes generales.
    static int currentGeneralMessage = 0;
    // Definiciones de identificadores
    static Hashtable DefinedIdentifiers = new Hashtable();
    // Solicitudes de identificadores
    static Hashtable RequiredIdentifiers = new Hashtable();
    // Tablas Hash de m�todos. Se definen tres tablas:
    // Definici�n de m�todos
    static Hashtable DefinedMethods = new Hashtable();
    // Solicitud de m�todos
    static Hashtable RequiredMethods = new Hashtable();
    // Implementaci�n de m�todos
    static Hashtable ImplementedMethods = new Hashtable();

    /*
     * Tabla para almacenar los mensajes que fueron generados en el an�lisis
     * sem�ntico. La tabla tiene un consecutivo y la descripci�n del mensaje.
     */
    static Hashtable SemanticMessages = new Hashtable();

    /*
     * Tabla para almacenar mensajes de errores no controlados ocurridos durante
     * la etapa de traducci�n. La tabla tiene un consecutivo y la descripci�n
     * del mensaje.
     */
    static Hashtable GeneralMessages = new Hashtable();

    /**
     * M�todo para almacenar en una tabla las definiciones de los diferentes
     * tipos de identificadores que se encuentran en la traducci�n.<P> El
     * registro tendr� una llave compuesta por la lista de procesos en los
     * cuales interviene el identificador, el tipo de inserci�n y el nombre del
     * identificador.
     *
     * @param nameProcess Nombre o lista de nombres de procesos.
     * @param nameIdentifier Nombre del
     * <Code>Identificador</Code>.
     * @param identifierType Tipo de
     * <Code>Identificador</Code>.
     *
     */
    public static void PutDefinedIdentifiers(String nameProcess, String nameIdentifier, String identifierType) {

        // Se arma la llave para la tabla de identificadores
        String key = nameProcess + "." + nameIdentifier;

        // Se guarda el identificador en la tabla
        DefinedIdentifiers.put(key, identifierType);

    }

    /**
     * M�todo para almacenar en una tabla las solicitudes de los diferentes
     * tipos de identificadores que se encuentran en la traducci�n.<P> El
     * registro tendr� una llave compuesta por la lista de procesos en los
     * cuales interviene el identificador y el nombre del identificador.
     *
     * @param nameProcess Nombre o lista de nombres de procesos.
     * @param typePut Tipo de inserci�n del
     * <Code>Identificador</Code>.
     * @param nameIdentifier Nombre del
     * <Code>Identificador</Code>.
     * @param identifierType Tipo de
     * <Code>Identificador</Code>.
     *
     */
    public static void PutRequiredIdentifiers(String nameProcess, String nameIdentifier) {

        // Se arma la llave para la tabla de identificadores
        String key = nameProcess + "." + nameIdentifier;

        // Se guarda el identificador en la tabla de solicitudes
        RequiredIdentifiers.put(key, "-");

    }

    /**
     * M�todo para ingresar un mensaje en la tabla de mensajes sem�nticos. La
     * llave del mensaje es el siguiente valor del contador de los mensajes
     * sem�nticos que han generado.
     *
     * @param message Mensaje producto del <I>An�lisis Sem�ntico</I>, para
     * ingresar en la tabla de mensaje sem�nticos.
     */
    public static void PutSemanticMessages(String message) {
        currentSemanticMessage += 1;
        SemanticMessages.put(new Integer(currentSemanticMessage), message);
    }

    /**
     * M�todo para ingresar un mensaje en la tabla de mensajes generales. La
     * llave del mensaje es el siguiente valor del contador de los mensajes
     * generales que han generado.
     *
     * @param message Mensaje general a ingresar en la tabla de mensajes
     * generales.
     */
    public static void PutGeneralMessages(String message) {
        currentGeneralMessage += 1;
        GeneralMessages.put(new Integer(currentGeneralMessage), message);
    }

    /**
     * M�todo para ingresar una definici�n de un m�todo <I>PiCO</I> en la tabla
     * de definiciones de m�todos. Se genera una definici�n de un m�todo cuando
     * se traduce un Nombre en la Lista de Nombres de un �mbito <I>PiCO</I>. La
     * llave del registro en la tabla est� compuesta por el nombre del m�todo y
     * el nombre del proceso o lista de procesos.
     *
     * @param nameMethod Nombre del m�todo a definir.
     * @param nameProcess Nombre del proceso o lista de procesos en donde fue
     * definido.
     */
    public static void PutDefinedMethods(String nameMethod, String nameProcess) {
        String key = nameMethod + "." + nameProcess;
        DefinedMethods.put(key, "D");
    }

    /**
     * M�todo para ingresar una solicitud de un m�todo <I>PiCO</I> en la tabla
     * de solicitudes de m�todos. Se genera una solicitud de un m�todo cuando se
     * traduce un Mensaje en un Envio de Mensaje <I>PiCO</I>. <P> La llave del
     * registro en la tabla est� compuesta por el nombre del m�todo y el nombre
     * del proceso o lista de procesos. El valor del registro es el n�mero de
     * par�metros con los cuales se realiz� la solicitud
     *
     * @param nameMethod Nombre del m�todo a definir.
     * @param nameProcess Nombre del proceso o lista de procesos en donde fue
     * definido.
     * @param nuParametros N�mero de par�metros con los cuales fue solicitado el
     * m�todo.
     */
    public static void PutRequiredMethods(String nameMethod, String nameProcess, String nuParametros) {
        String key = nameMethod + "." + nameProcess;
        RequiredMethods.put(key, nuParametros);
    }

    /**
     * M�todo para ingresar una implementaci�n de un m�todo <I>PiCO</I> en la
     * tabla de implementaciones de m�todos. Se genera una implementaci�n de un
     * m�todo cuando se traduce un M�todo en una Lista de M�todos dentro de un
     * Objeto <I>PiCO</I>. <P> La llave del registro en la tabla est� compuesta
     * por el nombre del m�todo y el nombre del proceso o lista de procesos. El
     * valor del registro es el n�mero de par�metros con los cuales se realiz�
     * la implementaci�n.
     *
     * @param nameMethod Nombre del m�todo a definir.
     * @param nameProcess Nombre del proceso o lista de procesos en donde fue
     * definido.
     * @param nuParametros N�mero de par�metros con los cuales fue implementado
     * el m�todo.
     */
    public static void PutImplementedMethods(String nameMethod, String nameProcess, String nuParametros) {
        String key = nameMethod + "." + nameProcess;
        ImplementedMethods.put(key, nuParametros);
    }

    /**
     * M�todo para recorrer la tabla de definiciones de identificadores y sacar
     * mensajes de advertencia. Solo se genera advertencia cuando se define un
     * identificador ya sea como variable o como par�metro y este no es usado en
     * el programa.
     */
    public static void SumaryIdentifiers() {

        // Valor correspondiente de un identificador en la tabla Hash.
        String identifierType = "";

        // Llave correspondiente de un identificador en la tabla Hash
        String identifierKey = "";

        // Nombre o lista de procesos
        String process = "";

        // Nombre del identificador.
        String identifierName = "";

        // Conjunto de la tabla hash de identificadores.
        Set hashTable = DefinedIdentifiers.entrySet();

        // Registros que se encuentran en la tabla Hash.
        Map.Entry identifierRecord = null;

        // Se encontro solicitud del identificador ??
        boolean founded = false;

        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            identifierRecord = (Map.Entry) iterator.next();
            identifierType = (String) identifierRecord.getValue();
            identifierKey = (String) identifierRecord.getKey();

            // Se obtiene el nombre del identificador
            identifierName = identifierKey.substring(identifierKey.indexOf(".") + 1);
            // Se obtiene la lista de procesos
            process = identifierKey.substring(0, identifierKey.indexOf("."));

            // Se busca una solicitud del identificador en la tabla de solicitudes
            founded = SearchIdentifiers(identifierName, process);

            // Si no se encuentra, genera mensaje de advertencia
            if (founded == false) {

                // Se adiciona el mensaje a la tabla hash de mensajes.
                if (process.indexOf(",") == -1) {
                    PutSemanticMessages("Advertencia : El identificador " + identifierName + " no es usado en el proceso " + process);
                } else {
                    PutSemanticMessages("Advertencia : El identificador " + identifierName + " no es usado en el proceso " + process.substring(process.lastIndexOf(",") + 1));
                }
            }
        }
    }

    /**
     * M�todo para buscar al menos una solicitud de un identificador.
     */
    public static boolean SearchIdentifiers(String definedIdentifier, String definitionProcess) {

        // Llave correspondiente de un identificador en la tabla Hash
        String identifierKey = "";

        // Nombre o lista de procesos
        String process = "";

        // Nombre del identificador.
        String identifierName = "";

        // Se encontro solicitud del identificador ??
        boolean founded = false;

        // Conjunto de la tabla hash de identificadores.
        Set hashTable = RequiredIdentifiers.entrySet();

        // Registros que se encuentran en la tabla Hash.
        Map.Entry identifierRecord = null;

        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            identifierRecord = (Map.Entry) iterator.next();
            identifierKey = (String) identifierRecord.getKey();

            // Se obtiene el nombre del identificador
            identifierName = identifierKey.substring(identifierKey.indexOf(".") + 1);

            if (identifierName.equals(definedIdentifier)) {

                // Se obtiene la lista de procesos
                process = identifierKey.substring(0, identifierKey.indexOf("."));

                // Se busca el proceso en la lista de procesos
                if (process.indexOf(definitionProcess) != -1) {
                    founded = true;
                    return founded;
                }

            }



        }
        return founded;
    }

    /**
     * M�todo que analiza la tabla de definiciones de m�todos para obtener
     * posibles mensajes sem�nticos de error o advertencia. El proceso es el
     * siguiente: <P> <Blockquote> 1. Obtiene cada m�todo registrado en la tabla
     * de definiciones de m�todos. <P> 2. Para cada m�todo encontrado, busca una
     * implementaci�n en la tabla de implementaciones, que se haya realizado en
     * el mismo proceso del �mbito en el cual fue definido el m�todo. (Ver
     * SearchImplementedMethod). </Blockquote>
     */
    public static void SumaryDefinedMethods() {

        // Valor del campo en un registro de la tabla
        String methodValue = "";

        // Valor de la llave en un registro de la tabla
        String methodKey = "";

        // Nombre de proceso
        String processName = "";

        // Nombre de m�todo
        String methodName = "";

        // Variable para almacenar el conjunto de la tabla hash
        Set hashTable = DefinedMethods.entrySet();

        // Variable para obtener cada uno de los registro que se encuentran en la tabla Hash.
        Map.Entry methodRecord = null;

        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            methodRecord = (Map.Entry) iterator.next();
            methodValue = (String) methodRecord.getValue();
            methodKey = (String) methodRecord.getKey();

            // Se obtiene el nombre del m�todo
            methodName = methodKey.substring(0, methodKey.indexOf("."));

            // Se obtiene el nombre del proceso
            processName = methodKey.substring(methodKey.indexOf(".") + 1);

            // Por cada m�todo que est� definido, se busca la implementaci�n del mismo
            SearchImplementedMethod(methodName, processName);
        }
    }

    /**
     * M�todo que busca en la tabla de implementaciones de m�todos un registro
     * que contenga el nombre del m�todo y el proceso dados como par�metros.<P>
     * Si el m�todo es encontrado, se elimina de la tabla de implementaciones y
     * se buscan solicitudes del m�todo en la tabla de solicitudes (Ver
     * SearchRequiredMethod). <P> Si el m�todo no es encontrado, se genera un
     * mensaje sem�ntico de advertencia.
     *
     * @param nameMethod Nombre del m�todo a buscar.
     * @param nameProcess Nombre del proceso a buscar.
     */
    public static void SearchImplementedMethod(String nameMethod,
            String nameProcess) {

        // Valor del campo en un registro de la tabla
        String methodValue = "";

        // Valor de la llave en un registro de la tabla
        String methodKey = "";

        // Nombre de proceso
        String processName = "";

        // Lista de procesos obtenidos
        String processList = "";

        // Nombre de m�todo
        String methodName = "";

        // Control de errores
        boolean founded = false;

        // Vector para almacenar las llaves de los registros a borrar
        Vector keys = new Vector();;

        // Variable para almacenar el conjunto de la tabla hash
        Set hashTable = ImplementedMethods.entrySet();

        // Variable para obtener cada uno de los registros que se encuentran en la tabla Hash.
        Map.Entry methodRecord = null;

        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            methodRecord = (Map.Entry) iterator.next();
            // El valor del registro corresponde al n�mero de par�metros
            methodValue = (String) methodRecord.getValue();
            methodKey = (String) methodRecord.getKey();

            // Se obtiene el nombre del m�todo
            methodName = methodKey.substring(0, methodKey.indexOf("."));

            // Si se encuentra el nombre del m�todo, se busca el nombre del proceso
            if (methodName.equals(nameMethod)) {

                // Se obtiene el nombre o lista de nombres de procesos
                processList = methodKey.substring(methodKey.indexOf(".") + 1);

                // Se busca el proceso en la lista
                if (processList.indexOf(nameProcess) != -1) {
                    founded = true;

                    // Adiciono la llave en el vector de llaves a borrar.
                    keys.addElement(methodKey);

                    // Busca en la tabla de solicitudes a m�todos si existe alguna
                    SearchRequiredMethod(methodName, nameProcess, methodValue);
                }

            }
        }
        if (founded == false) {
            PutSemanticMessages("Advertencia : El m�todo " + nameMethod + " no ha sido implementado en el proceso " + nameProcess);
        }

        // Se eliminan los registros encontrados
        int size = keys.size();
        for (int count = 0; count < size; count++) {
            ImplementedMethods.remove(keys.get(count));
        }
    }

    /**
     * M�todo que busca en la tabla de solicitudes de m�todos un registro que
     * contenga el nombre, el proceso y el n�mero de par�metros dados. <P> Si el
     * <Code>M�todo</Code> es encontrado, se elimina de la tabla de solicitudes.
     * Si el
     * <Code>M�todo</Code> no es encontrado, se genera un mensaje sem�ntico de
     * advertencia.
     *
     * @param nameMethod Nombre del m�todo a buscar.
     * @param nameProcess Nombre del proceso a buscar.
     * @param parameters Numero de par�metros a buscar.
     *
     */
    public static void SearchRequiredMethod(String nameMethod,
            String nameProcess,
            String parameters) {

        // Valor del campo en un registro de la tabla
        String methodValue = "";

        // Valor de la llave en un registro de la tabla
        String methodKey = "";

        // Nombre de proceso
        String processName = "";

        // Lista de procesos obtenidos
        String processList = "";

        // Nombre de m�todo
        String methodName = "";

        // Control de errores
        boolean founded = false;

        // Mensaje sem�ntico
        String message = "";

        // Vector para almacenar las llaves de los registros a borrar
        Vector keys = new Vector();;

        // Variable para almacenar el conjunto de la tabla hash
        Set hashTable = RequiredMethods.entrySet();

        // Variable para obtener cada uno de los registro que se encuentran en la tabla Hash.
        Map.Entry methodRecord = null;

        // Se construye mensaje sem�ntico
        if (nameProcess.indexOf("@") == -1) {
            message = "Advertencia : El m�todo " + nameMethod + ", local al proceso " + nameProcess;
            message = message + ", no est� presente en un antecedente ";
        } else {
            message = "Advertencia : El m�todo " + nameMethod + " implementado en el proceso ";
            message = message + nameProcess.substring(1) + " no est� presente en un antecedente";
        }
        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            methodRecord = (Map.Entry) iterator.next();
            // El valor del registro corresponde al n�mero de par�metros
            methodValue = (String) methodRecord.getValue();
            methodKey = (String) methodRecord.getKey();

            // Se obtiene el nombre del m�todo
            methodName = methodKey.substring(0, methodKey.indexOf("."));

            // Si se encuentra el nombre del m�todo, se busca el nombre del proceso
            if (methodName.equals(nameMethod)) {

                if (nameProcess.indexOf("@") == -1) {
                    // Se obtiene el nombre o lista de nombres de procesos
                    processList = methodKey.substring(methodKey.indexOf(".") + 1);

                    // Se busca el proceso en la lista
                    if (processList.indexOf(nameProcess) != -1) {
                        // Se valida que el n�mero de par�metros sea correcto
                        if (methodValue.equals(parameters)) {
                            founded = true;

                            // Adiciono la llave en el vector de llaves a borrar.
                            keys.addElement(methodKey);
                        } else {
                            PutSemanticMessages("Error       : N�mero de par�metros incorrecto en el llamado al m�todo " + nameMethod + " en el proceso " + methodKey.substring(methodKey.lastIndexOf(",") + 1));
                        }
                    }
                } else {
                    // Se valida que el n�mero de par�metros sea correcto
                    if (methodValue.equals(parameters)) {
                        founded = true;
                        // Adiciono la llave en el vector de llaves a borrar.
                        keys.addElement(methodKey);
                    }
                }
            }
        }
        if (founded == false) {
            PutSemanticMessages(message);
        }

        // Se eliminan los registros encontrados
        int size = keys.size();
        for (int count = 0; count < size; count++) {
            RequiredMethods.remove(keys.get(count));
        }
    }

    /**
     * M�todo que analiza la tabla de implementaciones de m�todos para obtener
     * posibles mensajes sem�nticos de error o advertencia. <P> Cuando este
     * proceso inicia, en la tabla de implementaciones solo quedan los m�todos
     * que son globales al programa, ya que los locales a un �mbito ya fueron
     * eliminados en el proceso de an�lisis de la tabla de definiciones de
     * m�todos. El proceso es el siguiente:<P> <Blockquote> 1. Obtiene cada
     * m�todo registrado en la tabla de implementaciones de m�todos.<P> 2. Para
     * cada m�todo encontrado, busca una solicitud en la tabla de solicitudes.
     * (Ver SearchImplementedMethod). </Blockquote>
     */
    public static void SumaryImplementedMethods() {

        // Valor de la llave en un registro de la tabla
        String methodKey = "";

        // Valor del campo (n�mero de par�metros)
        String methodValue = "";

        // Nombre de m�todo
        String methodName = "";

        // Cadena de m�todos buscados
        String searchedMethods = "";

        // Lista de procesos
        String processList = "";

        // Nombre de proceso
        String processName = "";

        // Variable para almacenar el conjunto de la tabla hash
        Set hashTable = ImplementedMethods.entrySet();

        // Variable para obtener cada uno de los registro que se encuentran en la tabla Hash.
        Map.Entry methodRecord = null;

        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            methodRecord = (Map.Entry) iterator.next();
            methodKey = (String) methodRecord.getKey();
            methodValue = (String) methodRecord.getValue();

            // Se obtiene el nombre del m�todo
            methodName = methodKey.substring(0, methodKey.indexOf("."));

            // Se obtiene lista de nombres de procesos
            processList = methodKey.substring(methodKey.indexOf(".") + 1);

            // Se obtiene el proceso en el cual se implement�
            processName = processList.substring(processList.lastIndexOf(",") + 1);

            // Se verifica si el m�todo ya fue buscado
            if (searchedMethods.indexOf(methodName + "." + methodValue) == -1) {

                // Por cada m�todo que est� implementado, se buscan solicitudes del mismo
                SearchRequiredMethod(methodName, "@" + processName, methodValue);

                // Pone el m�todo en la lista de m�todos buscados
                searchedMethods = searchedMethods + methodName + "." + methodValue + ", ";
            }
        }
    }

    /**
     * M�todo que analiza la tabla de solicitudes de m�todos para obtener
     * posibles mensajes sem�nticos de error o advertencia. <P> Cuando este
     * proceso inicia, en la tabla de solicitudes solo quedan las solicitudes a
     * m�todos que no han sido implementados o solicitudes que fueron realizadas
     * con un n�mero equivocado de par�metros, ya que las dem�s solicitudes ya
     * fueron eliminadas por sus correspondientes implementaciones en procesos
     * anteriores.<P> El proceso es el siguiente:<P> <Blockquote> 1. Obtiene
     * cada m�todo registrado en la tabla de solicitudes de m�todos. <P> 2. Para
     * cada m�todo encontrado, se genera un mensaje de Error sem�ntico.
     * </Blockquote>
     */
    public static void SumaryRequiredMethods() {

        // Valor de la llave en un registro de la tabla
        String methodKey = "";

        // Nombre de m�todo
        String methodName = "";

        // Lista de procesos
        String processList = "";

        // Nombre de proceso
        String processName = "";

        // Variable para almacenar el conjunto de la tabla hash
        Set hashTable = RequiredMethods.entrySet();

        // Variable para obtener cada uno de los registro que se encuentran en la tabla Hash.
        Map.Entry methodRecord = null;

        // Se usa un iterador para recorrer la tabla Hash
        for (Iterator iterator = hashTable.iterator(); iterator.hasNext();) {

            methodRecord = (Map.Entry) iterator.next();
            methodKey = (String) methodRecord.getKey();

            // Se obtiene el nombre del m�todo
            methodName = methodKey.substring(0, methodKey.indexOf("."));

            // Se obtiene lista de nombres de procesos
            processList = methodKey.substring(methodKey.indexOf(".") + 1);

            // Se obtiene el proceso desde el cual se realiz� la solicitud
            processName = processList.substring(processList.lastIndexOf(",") + 1);

            PutSemanticMessages("Error       : El m�todo " + methodName + " solicitado en el proceso " + processName + " no ha sido implementado");
        }
    }

    /**
     * M�todo que lanza la ejecuci�n de todos los an�lisis de las tablas hash
     * empleadas para el <I>An�lisis Sem�ntico</I> de la traducci�n <I>GraPiCO
     * Textual</I> - <I>C�lculo PiCO</I>.
     */
    public static void SumaryAllTables() {
        SumaryDefinedMethods();
        SumaryImplementedMethods();
        SumaryRequiredMethods();
        SumaryIdentifiers();
    }

    /**
     * M�todo que obtiene la tabla de mensajes sem�nticos generados en la etapa
     * de an�lisis.
     *
     * @return tabla hash de mensajes sem�nticos.
     *
     */
    public static Hashtable GetSemanticMessages() {
        return SemanticMessages;
    }

    /**
     * M�todo que obtiene la tabla de mensajes generales generados en la
     * traducci�n.
     *
     * @return tabla hash de mensajes generales.
     *
     */
    public static Hashtable GetGeneralMessages() {
        return GeneralMessages;
    }

    /**
     * M�todo que limpia todas las tablas hash empleadas en el <I>An�lisis
     * Sem�ntico</I> para garantizar que no persistan datos.
     *
     */
    public static void ClearAllHashtables() {
        DefinedIdentifiers.clear();
        RequiredIdentifiers.clear();
        SemanticMessages.clear();
        GeneralMessages.clear();
        DefinedMethods.clear();
        RequiredMethods.clear();
        ImplementedMethods.clear();
    }
}